import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Iterator;

import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JViewport;
import javax.swing.Scrollable;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;


public class MapPanelMelbourne extends SimpleMapPanel implements MouseMotionListener,
		Scrollable, ChangeListener, MouseListener {
	static final long serialVersionUID = 32L;
	static final boolean debugging = false;
	static final double RADIUS = 3.0;
	static final double NEARNESS = 1.0;
	static final int DRAG_TOLERANCE = 5;
	float[] dashes10 = { (float) (10.0 / scale), (float) (6.0 / scale) };
	static final int ALL_MODE = 0;
	private boolean roadCheckBox = true;
	private boolean railCheckBox = true;
	private boolean subCheckBox = true;
	private String selectedSuburb = "";
	// different tabs
	static final int SEARCH_MODE = 0;
	static final int COLOUR_MODE = 1;
	private int mode = ALL_MODE, saveMode = mode;
	private int tabmode = SEARCH_MODE;

	/*
	 * declared in SimpleMapPanel: protected MapData data; protected
	 * AffineTransform af, inverse; protected double scale = 10.0; //
	 * pixels/degree (dummy init value) protected int zoomFactor = 1; protected
	 * Rectangle2D.Double outerRect; protected Rectangle2D.Double innerRect; //
	 * approx what is currently visible (includes scrollbars) protected
	 * DecimalFormat df = new DecimalFormat("##0.000");
	 */
	private Rectangle vpRect; // current
	private Rectangle vpRequest; // pending request, null if none
	private JViewport viewport;
	private JLabel status;
	private Cursor savedCursor = new Cursor(Cursor.DEFAULT_CURSOR);

	private Color bg_colour = Color.RED;
	private Region currentRegion = null, selectedRegion = null;
	private Area selectedArea = null;
	private Road selectedRoad = null;
	private Rail selectedRail = null;
	private Point lastMouseDown; // for drag recognition
	private Point lastDragPos; // for dragging viewport
	private boolean dragging = false;
	private boolean zooming = false;

	private MovableTransport car;

	/**
	 * Initiallises the map display, sets up mouse button listeners.
	 * 
	 * @param status
	 *            is place to display mouse position in pixels and lonlat.
	 */
	public MapPanelMelbourne(JLabel status) {
		super();
		this.status = status;
		setPreferredSize(new Dimension(500, 500)); // <<<<<<
		addMouseListener(this);
		addMouseMotionListener(this);
	}
	

	public void setBGColour(Color colour) {
		this.bg_colour = colour;
	}

	public int getMode() {
		return mode;
	}

	public void setTabMode(int tabmode) {
		this.tabmode = tabmode;
	}

	public int getTabMode() {
		return tabmode;
	}

	public void mouseMoved(MouseEvent ev) {
		showWhereMouseIs(ev.getX(), ev.getY());
	}

	public void mouseDragged(MouseEvent ev) {
		showWhereMouseIs(ev.getX(), ev.getY());
		if (dragging) 
		{
			int dx = ev.getX() - lastDragPos.x; // typically +-1
			int dy = ev.getY() - lastDragPos.y; // ditto
			Point vpPos = getLocation();
			setLocation(vpPos.x + dx, vpPos.y + dy);
			lastDragPos = ev.getPoint(); // done this small drag
		} 
		else 
		{
			savedCursor = getCursor();
			setCursor(new Cursor(Cursor.HAND_CURSOR));
			dragging = true;
			lastDragPos = ev.getPoint();
		}
	}

	public void drawSuburb(Graphics2D g2) {
		if (subCheckBox) {

			for (Area value : data.getArea().values()) {

				Point2D.Double current = null;
				Point2D.Double prev = null;
				Point2D.Double first = null;

				for (Point2D.Double v : value.getPoints()) {
					current = new Point2D.Double(v.x, v.y);
					if (prev != null) {
							selectedArea = value;
							
							g2.setColor(Color.BLACK);
							g2.drawLine((int) prev.x, (int) prev.y,
								(int) current.x, (int) current.y);
					} else {

						first = current;
					}
					prev = current;

				}
			}
		}
	}

	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		if (data == null)
			return;
		Graphics2D g2 = (Graphics2D) g;
		g2.setColor(Color.BLACK);

		if (selectedArea != null && subCheckBox == true) {
			g2.setPaint(this.bg_colour);
			g2.fill(data.drawPolygon(selectedArea));
			
		}

		drawSuburb(g2);
		
	}

	public void drawPath(Graphics2D g2) {
		if (car != null) {
			g2.fillOval((int) car.getLocation().getX(), (int) car.getLocation()
					.getY(), 100, 100);

			if (car.getShortestPath().size() > 0) {
				Iterator<Vertex> iter = car.getShortestPath().iterator();
				Path2D.Double drawPath = new Path2D.Double();
				Point2D prev = null;
				while (iter.hasNext()) {
					Point2D pt = iter.next().getPoint2D();

					if (prev != null)
						drawPath.append(new Line2D.Double(pt, prev), true);
					prev = pt;
				}

				g2.setColor(new Color(120, 81, 169));
				g2.setStroke(new BasicStroke(32));
				g2.draw(drawPath);
			}
		}
	}

	

	
	

	public String createMessageBox(String message, Point2D.Double lonlat, String statistics) {
		String name = JOptionPane.showInputDialog(null, statistics, message);
		if (name == null) {
			return "";
		}
		return name;
	}

	public void setData(Region data) {
		this.data = data;
		outerRect = data.getBounds();
		innerRect = null; // established later
		zoomFactor = 1;
		mode = ALL_MODE;
		vpRect = viewport.getViewRect(); // pixels
		//setPreferredSize(new Dimension(vpRect.width, vpRect.height));
		setPreferredSize(new Dimension(4000, 4000));
		repaint();
	}


	

	/***
	 * 
	 * Added by terry need to fix zoom
	 * 
	 * @param a
	 */

	public void setSelectedArea(Area a) {
		if (selectedArea == a)
			return;
		selectedArea = a;
		repaint();
	}

	public void setSelectedRegion(Region r) {
		if (selectedRegion == r)
			return;
		selectedRegion = r;
		/*
		 * Point2D.Double pos = r.getRefPoint(); Point2D.Double pos2 = new
		 * Point2D.Double(); af.transform(pos, pos2); Rectangle vpReq = new
		 * Rectangle( (int)(pos2.getX() - 10), (int)(pos2.getY() - 10), 20, 20);
		 */
		Rectangle2D.Double bounds = r.getBounds();
		Point2D.Double pos = new Point2D.Double(bounds.getX(), bounds.getY()
				+ bounds.getHeight());
		Point2D.Double pos2 = new Point2D.Double();
		af.transform(pos, pos2);
		Rectangle vpReq = new Rectangle((int) pos2.getX(), (int) pos2.getY(),
				(int) (scale * bounds.getWidth()),
				(int) (scale * bounds.getHeight()));
		scrollRectToVisible(vpReq);
		repaint();
	}

	/**
	 * @param vp
	 *            expected to be the JViewport part of the JScrollPane that
	 *            contains this panel.
	 */
	public void setViewport(JViewport vp) {
		viewport = vp;
	}

	/**
	 * Displays the current mouse position in the 'status' label in both pixels
	 * and degrees. Because of zooming the x,y values can be large.
	 * 
	 * @param x
	 *            the horizontal mouse position within this panel.
	 * @param y
	 *            the vertical mouse position within this panel.
	 */
	private void showWhereMouseIs(int x, int y) {
		Point2D.Double pt = new Point2D.Double(x, y), lonlat = new Point2D.Double();
		
		ConvRegionFileToUTM conv = new ConvRegionFileToUTM();
		
		String result = "" + x + "," + y;
		if (inverse != null) {
			inverse.transform(pt, lonlat);
			result += "  " + conv.converting(lonlat) + " " + format(lonlat);
		}
		status.setText(result);
	}

	private int scCount = 0; // for debugging

	/**
	 * Respond to changes in the JViewport part of JScrollPane that contains
	 * this panel. Changes may be in position or size including zoomIn or
	 * zoomOut. Always fires PropertyChangeEvent for "viewport.Double", which is
	 * innerRect, the actual lon-lat visible.
	 * 
	 * @param e
	 *            the event whose source is checked vs viewport
	 * @see setViewport
	 */
	public void stateChanged(ChangeEvent e) {
		if (e.getSource() != viewport)
			return;
		vpRect = viewport.getViewRect(); // pixels
		
		Dimension ds = getSize();
		// System.out.print("size=" + ds.width + ", " + ds.height);
		Dimension d = getPreferredSize();
	
		if (ds.width != d.width)
			;// do nothing -- size change underway
		else if (vpRequest != null) {
			Rectangle vpReq = vpRequest;
			vpRequest = null; // prevent doing it twice
		
			// aug11 scrollRectToVisible(vpReq); // will recurse
		} else if (inverse != null) {
			// resize and scroll finished, so find vp in lon-lat
			Point2D.Double pt = new Point2D.Double(vpRect.x, vpRect.y
					+ vpRect.height), lonlat1 = new Point2D.Double(), lonlat2 = new Point2D.Double();
			inverse.transform(pt, lonlat1); // bot left
			pt.setLocation(vpRect.x + vpRect.width, vpRect.y);
			inverse.transform(pt, lonlat2); // top right
			Rectangle2D oldRect = innerRect;
			innerRect = new Rectangle2D.Double(lonlat1.getX(), lonlat1.getY(),
					lonlat2.getX() - lonlat1.getX(), lonlat2.getY()
							- lonlat1.getY());
			firePropertyChange("viewport.Double", oldRect, innerRect);
		}
	}

	
	



	

	
	/** Implementing Scrollable: prefer to see all */
	public Dimension getPreferredScrollableViewportSize() {
		return getPreferredSize();
	}

	/** Implementing Scrollable: do nothing */
	public boolean getScrollableTracksViewportWidth() {
		return false;
	}

	/** Implementing Scrollable: do nothing */
	public boolean getScrollableTracksViewportHeight() {
		return false;
	}

	/** Implementing Scrollable: unit one pixel */
	public int getScrollableUnitIncrement(Rectangle visibleRect,
			int orientation, int direction) {
		return 1;
	}

	/** Implementing Scrollable: jump by 10 pixels */
	public int getScrollableBlockIncrement(Rectangle visibleRect,
			int orientation, int direction) {
		return 10;
	}


	public AffineTransform getTranform() {
		return this.inverse;
	}

	public void mouseClicked(MouseEvent arg0) {
		// TODO Auto-generated method stub
	}

	public void mouseEntered(MouseEvent arg0) {
		// TODO Auto-generated method stub

	}

	public void mouseExited(MouseEvent arg0) {
		// TODO Auto-generated method stub

	}

	public void mousePressed(MouseEvent arg0) {
		// TODO Auto-generated method stub

	}

	public void mouseReleased(MouseEvent arg0) {
		// TODO Auto-generated method stub

	}

	public void addCar() {
		// TODO Auto-generated method stub
		//car = new MovableTransport(data.getRoad());
		//car.setPanel(this);
		//car.start();
	}
}

